/**
 * ReplyGenerator 属性测试
 * 
 * 使用 fast-check 进行属性测试，验证回复生成器的正确性
 */

import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import * as fc from 'fast-check';
import { ReplyGenerator } from './ReplyGenerator';
import { GitCodeApiService } from './GitCodeApiService';
import type { ParsedChat, EmotionalTone, ReplyStyle, ReplyOptions } from '../types';

// ============================================
// 常量定义
// ============================================

const LONG_TEXT_THRESHOLD = 200;
const MIN_REPLY_COUNT = 3;

// ============================================
// 生成器定义
// ============================================

/**
 * 生成有效的发送者名称
 */
const senderArbitrary = fc.string({ minLength: 1, maxLength: 15 })
  .filter(s => {
    const trimmed = s.trim();
    return trimmed.length > 0 && 
           !trimmed.includes(':') && 
           !trimmed.includes('：') &&
           !trimmed.includes('\n');
  })
  .map(s => s.trim());

/**
 * 生成有效的消息内容
 */
const contentArbitrary = fc.string({ minLength: 1, maxLength: 100 })
  .filter(s => {
    const trimmed = s.trim();
    return trimmed.length > 0 && !trimmed.includes('\n');
  })
  .map(s => s.trim());

/**
 * 生成情感基调
 */
const emotionalToneArbitrary: fc.Arbitrary<EmotionalTone> = fc.constantFrom(
  'happy', 'sad', 'comfort', 'funny', 'grateful', 'apologetic', 'neutral'
);

/**
 * 生成回复风格
 */
const replyStyleArbitrary: fc.Arbitrary<ReplyStyle> = fc.constantFrom('formal', 'casual');

/**
 * 生成有效的 ParsedChat 对象
 */
const parsedChatArbitrary: fc.Arbitrary<ParsedChat> = fc.tuple(
  fc.array(senderArbitrary, { minLength: 2, maxLength: 5 }),
  fc.array(contentArbitrary, { minLength: 2, maxLength: 10 }),
  emotionalToneArbitrary
).map(([senders, contents, emotionalTone]) => {
  const uniqueSenders = [...new Set(senders)];
  if (uniqueSenders.length < 2) {
    uniqueSenders.push(uniqueSenders[0] + '_other');
  }
  
  const messages = contents.map((content, i) => ({
    sender: uniqueSenders[i % uniqueSenders.length],
    content,
    isUser: i % uniqueSenders.length === 1
  }));
  
  return {
    messages,
    participants: uniqueSenders,
    emotionalTone
  };
});

/**
 * 生成回复选项
 */
const replyOptionsArbitrary: fc.Arbitrary<ReplyOptions> = fc.record({
  style: replyStyleArbitrary,
  goal: fc.constantFrom('comfort', 'decline', 'thank', 'apologize', 'congratulate', 'custom', undefined),
  maxLength: fc.option(fc.integer({ min: 50, max: 500 }), { nil: undefined })
});

/**
 * 生成目标描述文本
 */
const goalDescriptionArbitrary = fc.string({ minLength: 5, maxLength: 100 })
  .filter(s => s.trim().length >= 5)
  .map(s => s.trim());

/**
 * 生成长文本内容（超过200字符）
 */
const longContentArbitrary = fc.string({ minLength: LONG_TEXT_THRESHOLD + 1, maxLength: 500 })
  .filter(s => s.trim().length > LONG_TEXT_THRESHOLD);

/**
 * 生成模拟的 API 回复（包含多条回复）
 */
const mockApiResponseArbitrary = fc.array(
  fc.string({ minLength: 10, maxLength: 150 }).filter(s => s.trim().length >= 10),
  { minLength: 3, maxLength: 5 }
).map(replies => {
  return replies.map((r, i) => `回复${i + 1}: ${r.trim()}`).join('\n---\n');
});

/**
 * 生成包含长文本的模拟 API 回复
 */
const mockApiResponseWithLongTextArbitrary = fc.tuple(
  longContentArbitrary,
  fc.array(
    fc.string({ minLength: 10, maxLength: 100 }).filter(s => s.trim().length >= 10),
    { minLength: 2, maxLength: 4 }
  )
).map(([longContent, shortContents]) => {
  const replies = [longContent, ...shortContents];
  return replies.map((r, i) => `回复${i + 1}: ${r.trim()}`).join('\n---\n');
});

// ============================================
// 测试辅助函数
// ============================================

/**
 * 创建带有模拟 API 的 ReplyGenerator
 */
function createMockedGenerator(mockResponse: string): ReplyGenerator {
  const mockApiService = new GitCodeApiService({ apiKey: 'test-key' });
  vi.spyOn(mockApiService, 'chat').mockResolvedValue(mockResponse);
  return new ReplyGenerator(mockApiService);
}

// ============================================
// Property 1: 回复生成数量保证
// ============================================

describe('ReplyGenerator Property Tests', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  afterEach(() => {
    vi.restoreAllMocks();
  });

  /**
   * **Feature: smart-chat-reply, Property 1: 回复生成数量保证**
   * **Validates: Requirements 1.2**
   * 
   * 对于任意有效的聊天上下文，调用 ReplyGenerator.generate() 应返回至少3条候选回复
   */
  describe('Property 1: 回复生成数量保证', () => {
    it('generate() 应返回至少3条回复', async () => {
      await fc.assert(
        fc.asyncProperty(
          parsedChatArbitrary,
          replyOptionsArbitrary,
          mockApiResponseArbitrary,
          async (context, options, mockResponse) => {
            const generator = createMockedGenerator(mockResponse);
            
            const replies = await generator.generate(context, options);
            
            // 验证返回至少 MIN_REPLY_COUNT 条回复
            expect(replies.length).toBeGreaterThanOrEqual(MIN_REPLY_COUNT);
            
            // 验证每条回复都有必要的字段
            for (const reply of replies) {
              expect(reply.id).toBeDefined();
              expect(typeof reply.id).toBe('string');
              expect(reply.content).toBeDefined();
              expect(typeof reply.content).toBe('string');
              expect(reply.content.length).toBeGreaterThan(0);
              expect(reply.style).toBeDefined();
              expect(reply.emotionalTone).toBeDefined();
            }
          }
        ),
        { numRuns: 100 }
      );
    });

    it('当 API 返回空响应时也应返回至少3条默认回复', async () => {
      await fc.assert(
        fc.asyncProperty(
          parsedChatArbitrary,
          replyOptionsArbitrary,
          async (context, options) => {
            // 模拟 API 返回空响应
            const generator = createMockedGenerator('');
            
            const replies = await generator.generate(context, options);
            
            expect(replies.length).toBeGreaterThanOrEqual(MIN_REPLY_COUNT);
          }
        ),
        { numRuns: 50 }
      );
    });
  });


  // ============================================
  // Property 2: 双风格选项保证
  // ============================================

  /**
   * **Feature: smart-chat-reply, Property 2: 双风格选项保证**
   * **Validates: Requirements 2.2**
   * 
   * 对于任意目标描述输入，ReplyGenerator.generateFromGoal() 应返回包含 formal 和 casual 两种风格的回复选项
   */
  describe('Property 2: 双风格选项保证', () => {
    it('generateFromGoal() 应返回包含两种风格的回复', async () => {
      await fc.assert(
        fc.asyncProperty(
          goalDescriptionArbitrary,
          replyOptionsArbitrary,
          mockApiResponseArbitrary,
          async (goal, options, mockResponse) => {
            const generator = createMockedGenerator(mockResponse);
            
            const replies = await generator.generateFromGoal(goal, options);
            
            // 验证返回至少 MIN_REPLY_COUNT 条回复
            expect(replies.length).toBeGreaterThanOrEqual(MIN_REPLY_COUNT);
            
            // 收集所有风格
            const styles = new Set(replies.map(r => r.style));
            
            // 验证包含两种风格
            expect(styles.has('formal')).toBe(true);
            expect(styles.has('casual')).toBe(true);
          }
        ),
        { numRuns: 100 }
      );
    });
  });

  // ============================================
  // Property 3: 长文本精简版本
  // ============================================

  /**
   * **Feature: smart-chat-reply, Property 3: 长文本精简版本**
   * **Validates: Requirements 2.4**
   * 
   * 对于任意生成的回复，如果 content 长度超过200字符，则该回复对象必须包含非空的 shortVersion 字段
   */
  describe('Property 3: 长文本精简版本', () => {
    it('长文本回复应包含 shortVersion 字段', async () => {
      await fc.assert(
        fc.asyncProperty(
          parsedChatArbitrary,
          replyOptionsArbitrary,
          mockApiResponseWithLongTextArbitrary,
          async (context, options, mockResponse) => {
            const generator = createMockedGenerator(mockResponse);
            
            const replies = await generator.generate(context, options);
            
            // 检查每条回复
            for (const reply of replies) {
              if (reply.content.length > LONG_TEXT_THRESHOLD) {
                // 长文本必须有 shortVersion
                expect(reply.shortVersion).toBeDefined();
                expect(typeof reply.shortVersion).toBe('string');
                expect(reply.shortVersion!.length).toBeGreaterThan(0);
                // shortVersion 应该比原文短
                expect(reply.shortVersion!.length).toBeLessThanOrEqual(LONG_TEXT_THRESHOLD);
              }
            }
          }
        ),
        { numRuns: 100 }
      );
    });

    it('短文本回复不需要 shortVersion 字段', async () => {
      await fc.assert(
        fc.asyncProperty(
          parsedChatArbitrary,
          replyOptionsArbitrary,
          // 生成只包含短文本的响应
          fc.array(
            fc.string({ minLength: 10, maxLength: 100 }).filter(s => s.trim().length >= 10 && s.trim().length <= LONG_TEXT_THRESHOLD),
            { minLength: 3, maxLength: 5 }
          ).map(replies => replies.map((r, i) => `回复${i + 1}: ${r.trim()}`).join('\n---\n')),
          async (context, options, mockResponse) => {
            const generator = createMockedGenerator(mockResponse);
            
            const replies = await generator.generate(context, options);
            
            // 检查短文本回复
            for (const reply of replies) {
              if (reply.content.length <= LONG_TEXT_THRESHOLD) {
                // 短文本不应该有 shortVersion（或者为 undefined）
                expect(reply.shortVersion).toBeUndefined();
              }
            }
          }
        ),
        { numRuns: 50 }
      );
    });
  });
});
